// SPDX-License-Identifier: GPL-2.0
#include <mach/csp.h>
#include "uart_operate.h"

static void csp_uart_div_latch_access(void *base, int access)
{
	u32 val = !!access ? 1 : 0;
	reg_uart_ctl_t temp;
	temp.val = readl(base + LOMBO_UART_CTL);
	temp.bits.dae = val;
	writel(temp.val, base + LOMBO_UART_CTL);
}

static void csp_uart_divisor_latch(void *base, int div_int, int div_fra)
{
	reg_uart_bd_t temp;
	temp.val = 0;
	temp.bits.bdi = div_int;
	temp.bits.bdf = div_fra;
	writel(temp.val, base + LOMBO_UART_BD);
}

void csp_uart_tx_en(void *base, int enable)
{
	u32 val = !!enable ? 1 : 0;
	reg_uart_ctl_t temp;
	temp.val = readl(base + LOMBO_UART_CTL);
	temp.bits.tx_start = val;
	writel(temp.val, base + LOMBO_UART_CTL);
}

void csp_uart_rx_en(void *base, int enable)
{
	u32 val = !!enable ? 1 : 0;
	reg_uart_ctl_t temp;
	temp.val = readl(base + LOMBO_UART_CTL);
	temp.bits.rx_start = val;
	writel(temp.val, base + LOMBO_UART_CTL);
}

void csp_uart_baud_rate_config(void *base, int clk_in, int baud)
{
	u32 div_int, div_fra;
	u32 res; /* division remainder */
	u32 div_fra_mod;
	u32 div_fra_percent;

	div_int = clk_in / (16 * baud);
	res     = clk_in % (16 * baud);
	div_fra = (res * 16) / (16 * baud);
	div_fra_mod = (res * 16) % (16 * baud);

	div_fra_percent = div_fra_mod * 100 / (16 * baud);
	if (div_fra_percent > 50)
		div_fra = div_fra + 1;

	/* disable tx and rx start */
	csp_uart_tx_en(base, 0);
	csp_uart_rx_en(base, 0);

	/* enable divisor latch access */
	csp_uart_div_latch_access(base, 1);

	/* set divisor latch */
	csp_uart_divisor_latch(base, div_int, div_fra);

	/* disable divisor latch access */
	csp_uart_div_latch_access(base, 0);

	/* enable tx and rx start */
	csp_uart_tx_en(base, 1);
	csp_uart_rx_en(base, 1);
}

void csp_uart_putchar(void *base, int c)
{
	writeb(c, base + LOMBO_UART_TXD);
}

u32 csp_uart_getchar(void *base)
{
	return readl(base + LOMBO_UART_RXD);
}

void csp_uart_dls_config(void *base, int bits)
{
	u32 val = UART_CTL_DLS_3;
	reg_uart_ctl_t temp;

	if (bits == 5)
		val = UART_CTL_DLS_0;
	else if (bits == 6)
		val = UART_CTL_DLS_1;
	else if (bits == 7)
		val = UART_CTL_DLS_2;

	/* disable tx and rx start */
	csp_uart_tx_en(base, 0);
	csp_uart_rx_en(base, 0);

	temp.val = readl(base + LOMBO_UART_CTL);
	temp.bits.dls = val;
	writel(temp.val, base + LOMBO_UART_CTL);

	/* enable tx and rx start */
	csp_uart_tx_en(base, 1);
	csp_uart_rx_en(base, 1);
}

void csp_uart_stop_config(void *base, int stop)
{
	u32 val = !!stop ? UART_CTL_STOP_1 : UART_CTL_STOP_0;
	reg_uart_ctl_t temp;

	/* disable tx and rx start */
	csp_uart_tx_en(base, 0);
	csp_uart_rx_en(base, 0);

	temp.val = readl(base + LOMBO_UART_CTL);
	temp.bits.stop = val;
	writel(temp.val, base + LOMBO_UART_CTL);

	/* enable tx and rx start */
	csp_uart_tx_en(base, 1);
	csp_uart_rx_en(base, 1);
}

void csp_uart_parity_enable(void *base, int parity)
{
	u32 val = !!parity ? UART_CTL_PARITY_EN_1 : UART_CTL_PARITY_EN_0;
	reg_uart_ctl_t temp;

	/* disable tx and rx start */
	csp_uart_tx_en(base, 0);
	csp_uart_rx_en(base, 0);

	temp.val = readl(base + LOMBO_UART_CTL);
	temp.bits.parity_en = val;
	writel(temp.val, base + LOMBO_UART_CTL);

	/* enable tx and rx start */
	csp_uart_tx_en(base, 1);
	csp_uart_rx_en(base, 1);
}

void csp_uart_set_parity_odd(void *base, int odd)
{
	u32 val = !!odd ? UART_CTL_PARITY_SEL_1 : UART_CTL_PARITY_SEL_0;
	reg_uart_ctl_t temp;

	/* disable tx and rx start */
	csp_uart_tx_en(base, 0);
	csp_uart_rx_en(base, 0);

	temp.val = readl(base + LOMBO_UART_CTL);
	temp.bits.parity_sel = val;
	writel(temp.val, base + LOMBO_UART_CTL);

	/* enable tx and rx start */
	csp_uart_tx_en(base, 1);
	csp_uart_rx_en(base, 1);
}

void csp_uart_reset(void *base)
{
	reg_uart_ctl_t temp;
	temp.val = readl(base + LOMBO_UART_CTL);
	temp.bits.reset = 1;
	writel(temp.val, base + LOMBO_UART_CTL);
}

void csp_uart_set_break_ctrl(void *base, u32 bc)
{
	u32 val = !!bc ? 1 : 0;
	reg_uart_ctl_t temp;
	temp.val = readl(base + LOMBO_UART_CTL);
	temp.bits.break_ctl = val;
	writel(temp.val, base + LOMBO_UART_CTL);
}

u32 csp_uart_is_auto_flow(void *base)
{
	reg_uart_ctl_t temp;

	/* disable tx and rx start */
	csp_uart_tx_en(base, 0);
	csp_uart_rx_en(base, 0);

	temp.val = readl(base + LOMBO_UART_CTL);

	/* enable tx and rx start */
	csp_uart_tx_en(base, 1);
	csp_uart_rx_en(base, 1);

	return temp.bits.afce;
}

#ifdef CONFIG_ARCH_LOMBO_N7V5
void csp_uart_rts_de_assert_threshold_cfg(void *base, u32 rts_de_val)
{
	reg_uart_rts_th_cfg_t temp;

	temp.val = readl(base + LOMBO_UART_RTS_TH_CFG);
	temp.bits.rts_de_a_thr = rts_de_val;
	writel(temp.val, base + LOMBO_UART_RTS_TH_CFG);
}

void csp_uart_rts_assert_threshold_cfg(void *base, u32 rts_a_val)
{
	reg_uart_rts_th_cfg_t temp;

	temp.val = readl(base + LOMBO_UART_RTS_TH_CFG);
	temp.bits.rts_a_thr = rts_a_val;
	writel(temp.val, base + LOMBO_UART_RTS_TH_CFG);
}
#else
void csp_uart_rts_de_assert_threshold_cfg(void *base, u32 rts_de_val)
{
}
void csp_uart_rts_assert_threshold_cfg(void *base, u32 rts_a_val)
{
}
#endif

void csp_uart_set_auto_flow(void *base, int afce)
{
	u32 val = !!afce ? UART_CTL_AFCE_1 : UART_CTL_AFCE_0;
	reg_uart_ctl_t temp;

	/* disable tx and rx start */
	csp_uart_tx_en(base, 0);
	csp_uart_rx_en(base, 0);

	temp.val = readl(base + LOMBO_UART_CTL);
	temp.bits.afce = val;
	writel(temp.val, base + LOMBO_UART_CTL);

	/* enable tx and rx start */
	csp_uart_tx_en(base, 1);
	csp_uart_rx_en(base, 1);
}

void csp_uart_tx_fifo_trg_config(void *base, int fill_level)
{
	reg_uart_fifo_ctl_t temp;
	temp.val = readl(base + LOMBO_UART_FIFO_CTL);
	temp.bits.tx_fifo_th = fill_level;
	writel(temp.val, base + LOMBO_UART_FIFO_CTL);
}

void csp_uart_rx_fifo_trg_config(void *base, int fill_level)
{
	reg_uart_fifo_ctl_t temp;
	temp.val = readl(base + LOMBO_UART_FIFO_CTL);
	temp.bits.rx_fifo_th = fill_level;
	writel(temp.val, base + LOMBO_UART_FIFO_CTL);
}

void csp_uart_tx_fifo_reset(void *base)
{
	reg_uart_fifo_ctl_t temp;
	temp.val = readl(base + LOMBO_UART_FIFO_CTL);
	temp.bits.tx_fifo_rst = 1;
	writel(temp.val, base + LOMBO_UART_FIFO_CTL);
}

void csp_uart_rx_fifo_reset(void *base)
{
	reg_uart_fifo_ctl_t temp;
	temp.val = readl(base + LOMBO_UART_FIFO_CTL);
	temp.bits.rx_fifo_rst = 1;
	writel(temp.val, base + LOMBO_UART_FIFO_CTL);
}

u32 csp_uart_get_sr(void *base)
{
	return readl(base + LOMBO_UART_STATUS);
}

u32 csp_uart_tx_empty(void *base)
{
	reg_uart_fifo_status_t temp;
	temp.val = readl(base + LOMBO_UART_FIFO_STATUS);
	return !temp.bits.tx_fifo_fill;
}

u32 csp_uart_rx_empty(void *base)
{
	reg_uart_fifo_status_t temp;
	temp.val = readl(base + LOMBO_UART_FIFO_STATUS);
	return temp.bits.rx_fifo_empty;
}

u32 csp_uart_is_cts(void *base)
{
	reg_uart_status_t temp;
	temp.val = readl(base + LOMBO_UART_STATUS);
	return temp.bits.cts_line_sta;
}

u32 csp_uart_tx_fifo_not_full(void *base)
{
	reg_uart_fifo_status_t temp;
	temp.val = readl(base + LOMBO_UART_FIFO_STATUS);
	return !temp.bits.tx_fifo_full;
}

u32 csp_uart_irq_save(void *base)
{
	return readl(base + LOMBO_UART_INT_EN);
}

void csp_uart_irq_restore(void *base, u32 irq)
{
	writel(irq, base + LOMBO_UART_INT_EN);
}

void csp_uart_irq_disable(void *base)
{
	writel(0x0, base + LOMBO_UART_INT_EN);
}

void csp_uart_tx_irq_config(void *base, u32 enable)
{
	u32 val = !!enable ? 1 : 0;
	reg_uart_int_en_t temp;
	temp.val = readl(base + LOMBO_UART_INT_EN);
	temp.bits.tx_empty = val;
	writel(temp.val, base + LOMBO_UART_INT_EN);
}

void csp_uart_rx_irq_disable(void *base)
{
	u32 val = readl(base + LOMBO_UART_INT_EN);

	val &= ~(LOMBO_UART_RX_BI_E | LOMBO_UART_RX_FE_E
		| LOMBO_UART_RX_PE_E | LOMBO_UART_RX_OV_E);
	writel(val, base + LOMBO_UART_INT_EN);
}

void csp_uart_irq_enable(void *base)
{
	u32 val = readl(base + LOMBO_UART_INT_EN);

	val &= ~(LOMBO_UART_RX_BI_E | LOMBO_UART_RX_FE_E
		| LOMBO_UART_RX_PE_E | LOMBO_UART_RX_OV_E
		| LOMBO_UART_RX_FULL_E | LOMBO_UART_TX_EMPTY_E);
	val |= LOMBO_UART_RX_BI_E | LOMBO_UART_RX_FE_E
		| LOMBO_UART_RX_PE_E | LOMBO_UART_RX_OV_E
		| LOMBO_UART_RX_FULL_E | LOMBO_UART_TX_EMPTY_E;

	writel(val, base + LOMBO_UART_INT_EN);
}

u32 csp_uart_is_tx_empty_trg(void *base)
{
	reg_uart_int_pd_t temp;
	temp.val = readl(base + LOMBO_UART_INT_PD);
	return temp.bits.tx_empty;
}

u32 csp_uart_is_rx_or_data_avl(void *base)
{
	u32 val = readl(base + LOMBO_UART_INT_PD);

	val &= (LOMBO_UART_RX_BI | LOMBO_UART_RX_FE | LOMBO_UART_RX_PE
		| LOMBO_UART_RX_OV | LOMBO_UART_RX_FULL);

	return val != 0 ? 1 : 0;
}

u32 csp_uart_is_cts_pend(void *base)
{
	u32 val = readl(base + LOMBO_UART_INT_PD);

	val &= LOMBO_UART_CTS;

	return val != 0 ? 1 : 0;
}

u32 csp_uart_is_tx_done_pend(void *base)
{
	u32 val = readl(base + LOMBO_UART_INT_PD);

	val &= LOMBO_UART_TX_DONE;

	return val != 0 ? 1 : 0;
}

void csp_uart_clear_tx_empty_trg(void *base)
{
	reg_uart_int_clr_t temp;
	temp.val = readl(base + LOMBO_UART_INT_CLR);
	temp.bits.tx_empty = 1;
	writel(temp.val, base + LOMBO_UART_INT_CLR);
}

void csp_uart_clear_rx_or_data_avl(void *base)
{
	u32 val = LOMBO_UART_RX_BI_CLR | LOMBO_UART_RX_FE_CLR
		| LOMBO_UART_RX_PE_CLR | LOMBO_UART_RX_OV_CLR
		| LOMBO_UART_RX_FULL_CLR;

	writel(val, base + LOMBO_UART_INT_CLR);
}

void csp_uart_clear_cts(void *base)
{
	reg_uart_int_clr_t temp;
	temp.val = readl(base + LOMBO_UART_INT_CLR);
	temp.bits.cts = 1;
	writel(temp.val, base + LOMBO_UART_INT_CLR);
}

void csp_uart_clear_tx_done_pend(void *base)
{
	reg_uart_int_clr_t temp;
	temp.val = readl(base + LOMBO_UART_INT_CLR);
	temp.bits.tx_done = 1;
	writel(temp.val, base + LOMBO_UART_INT_CLR);
}

u32 csp_uart_get_tx_fill_level(void *base)
{
	reg_uart_fifo_status_t temp;
	temp.val = readl(base + LOMBO_UART_FIFO_STATUS);
	return temp.bits.tx_fifo_fill;
}

u32 csp_uart_get_rx_fill_level(void *base)
{
	reg_uart_fifo_status_t temp;
	temp.val = readl(base + LOMBO_UART_FIFO_STATUS);
	return temp.bits.rx_fifo_fill;
}

void csp_uart_rs485_enable(void *base, u32 enable)
{
	u32 val = !!enable ? 1 : 0;
	reg_uart_ctl_t temp;

	/* disable tx and rx start */
	csp_uart_tx_en(base, 0);
	csp_uart_rx_en(base, 0);

	temp.val = readl(base + LOMBO_UART_CTL);
	temp.bits.rs485_en = val;
	writel(temp.val, base + LOMBO_UART_CTL);

	/* enable tx and rx start */
	csp_uart_tx_en(base, 1);
	csp_uart_rx_en(base, 1);
}

u32 csp_uart_rs485_mode_config(void *base, enum uart_rs485_mode mode)
{
	u32 val = 0;
	reg_uart_ctl_t temp;

	if (mode == FULL_DP_MODE)
		val = UART_CTL_RS485_MODE_0;
	else if (mode == SW_HALF_DP_MODE)
		val = UART_CTL_RS485_MODE_1;
	else if (mode == HW_HALF_DP_MODE)
		val = UART_CTL_RS485_MODE_2;

	/* disable tx and rx start */
	csp_uart_tx_en(base, 0);
	csp_uart_rx_en(base, 0);

	temp.val = readl(base + LOMBO_UART_CTL);
	temp.bits.rs485_mode = val;
	writel(temp.val, base + LOMBO_UART_CTL);

	/* enable tx and rx start */
	csp_uart_tx_en(base, 1);
	csp_uart_rx_en(base, 1);

	return temp.bits.rs485_mode;
}

void csp_uart_rs485_re_pol_enable(void *base, u32 enable)
{
	u32 val = !!enable ? 1 : 0;
	reg_uart_ctl_t temp;

	/* disable tx and rx start */
	csp_uart_tx_en(base, 0);
	csp_uart_rx_en(base, 0);

	temp.val = readl(base + LOMBO_UART_CTL);
	temp.bits.rs485_re_pol = val;
	writel(temp.val, base + LOMBO_UART_CTL);

	/* enable tx and rx start */
	csp_uart_tx_en(base, 1);
	csp_uart_rx_en(base, 1);
}

void csp_uart_rs485_de_pol_enable(void *base, u32 enable)
{
	u32 val = !!enable ? 1 : 0;
	reg_uart_ctl_t temp;

	/* disable tx and rx start */
	csp_uart_tx_en(base, 0);
	csp_uart_rx_en(base, 0);

	temp.val = readl(base + LOMBO_UART_CTL);
	temp.bits.rs485_de_pol = val;
	writel(temp.val, base + LOMBO_UART_CTL);

	/* enable tx and rx start */
	csp_uart_tx_en(base, 1);
	csp_uart_rx_en(base, 1);
}

u32 csp_uart_rs485_de_enable(void *base, u32 enable)
{
	u32 val = !!enable ? 1 : 0;
	reg_uart_ctl_t temp;

	temp.val = readl(base + LOMBO_UART_CTL);
	temp.bits.rs485_de_en = val;
	writel(temp.val, base + LOMBO_UART_CTL);

	return temp.bits.rs485_de_en;
}

u32 csp_uart_rs485_re_enable(void *base, u32 enable)
{
	u32 val = !!enable ? 1 : 0;
	reg_uart_ctl_t temp;

	temp.val = readl(base + LOMBO_UART_CTL);
	temp.bits.rs485_re_en = val;
	writel(temp.val, base + LOMBO_UART_CTL);

	return temp.bits.rs485_re_en;
}

void csp_uart_tx_dma_threshold_cfg(void *base, u32 val)
{
	reg_uart_dma_ctl_t temp;

	temp.val = readl(base + LOMBO_UART_DMA_CTL);
	temp.bits.tdma_th = val;
	writel(temp.val, base + LOMBO_UART_DMA_CTL);
}

u32 csp_uart_tx_dma_enable(void *base, u32 enable)
{
	u32 val = !!enable ? 1 : 0;
	reg_uart_dma_ctl_t temp;

	temp.val = readl(base + LOMBO_UART_DMA_CTL);
	temp.bits.tdma_e  = val;
	writel(temp.val, base + LOMBO_UART_DMA_CTL);

	return temp.bits.tdma_e;
}

